/****************************************************************
 * I am the AC 130 vs Pirate game. I allow the user to control an 
 * AC130 gunship in the hunt for Modern day pirates. The player  
 * will be allowed to roam free across a section of ocean/coastline.  
 * In the water there will be four types of vessels: Merchant  
 * ships that the player must protect, Pirate ships that the player  
 * must identify and destroy, Fishing boats that look like Pirate  
 * ships that the player must not destroy, and Friendly Navy ships  
 * that can capture pirates for extra points. Time permitting there  
 * may also be ground targets to attack. 
 *
 * Key elements of this project will be the Player/AC130, the Ships,  
 * the ShipAI system, the collision grid, the debris controller, the  
 * Ground/Water, Clouds, and scoring system. This project will make  
 * use of an external text file to store useful game parameters,  
 * thus allowing us to play balance the game without recompiling  
 * the executable. 
 *****************************************************************
 * I am the Plane Class. I contain all the data needed to Control
 * and Render a single Plane in the game world. The Player will control
 * the plane with the Mouse and Keyboard. Each Plane will have a
 * type and attributes set by that type. All playable aircraft will
 * be an instance of this class so
 *****************************************************************/

#include "Plane.h"

#include <string.h>
#include <math.h>

/// I am the constructor. I createt the initial sprites for the 
/// plane object.
Plane::Plane(){

	// physical values
	center = new Point;
	heading = new Point;
	aim = new Point;
	helper = new Point;
		// set initial movement variables
	speed = 0;
	strafe = 0;
	angle = 0;
	turnRate = 0;

	// initilize sprites to something
	for(int itor = 0; itor < PLANEMAXANIM; itor++){
		imgPlane[itor] = create_bitmap(32,32);
		imgTurret[itor] = create_bitmap(32,32);
	}
		// no pane bigger than 256 we hope
	render = create_bitmap(256,256);

	curTurSprite = curPlaneSprite = 0;
		// input toggles
	inLeft = false;
	inRight = false;
	inUp = false;
	inDown = false;
	inLstrafe = false;
	inRstrafe = false;
	inShoot = false;
	rendZoom = 1;
		
}

/// destructor 
Plane::~Plane(){
	for(int itor = 0; itor < PLANEMAXANIM; itor++){
		destroy_bitmap(imgPlane[itor]);
		destroy_bitmap(imgTurret[itor]);
	}
	destroy_bitmap(render);
	delete(center);
	delete(heading);
	delete(aim);
	delete(helper);
}

/// I am the Init funciton. I recieve all external pointers 
/// from the Game world.
void Plane::Init(GameData *exGd, BulletCtrl * exBulletCtrl){
	gd = exGd;
	myBulletCtrl = exBulletCtrl;
		// load C130 data
	SetType("plC130");
}

/// I am the plane variable setter. I use the prefix
/// supplied in arg1 in conjunction with gd to fill 
/// the internal variables of this plane these variables
/// dictate things from preformance to where to load sprites
void Plane::SetType(char * pre){	
	char total[256];	// editable string
	strncpy(total, pre, 64);
	strncat(total, "MaxSpeed", 128);
	MaxSpeed = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "MinSpeed", 128);
	MinSpeed = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "AvgSpeed", 128);
	AvgSpeed = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "DltSpeed", 128);
	DltSpeed = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "MaxTurn", 128);
	MaxTurn = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "DltTurn", 128);
	DltTurn = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "canStrafe", 128);
	canStrafe = (gd->GetStr(total)[0] == 't');	// helicopters only
		// weapon info
	strncpy(total, pre, 64);
	strncat(total, "TurMinAng", 128);
	TurMinAng = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "TurMaxAng", 128);
	TurMaxAng = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "TurMinRange", 128);
	TurMinRange = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "TurMaxRange", 128);
	TurMaxRange = gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "TurBullet", 128);
	TurBullet = (int)(gd->GetNum(total));	// bullet type
	strncpy(total, pre, 64);
	strncat(total, "TurRefire", 128);
	TurRefire = (int)(gd->GetNum(total));
	strncpy(total, pre, 64);
	strncat(total, "TurBulPerShot", 128);
	TurBulPerShot = (int)(gd->GetNum(total));
	strncpy(total, pre, 64);
	strncat(total, "TurSprayAng", 128);
	TurSprayAng= gd->GetNum(total);
	strncpy(total, pre, 64);
	strncat(total, "TurBlindShot", 128);
	TurBlindShot = (gd->GetStr(total)[0] == 't');
		// load image data
	strncpy(total, pre, 64);
	strncat(total, "SpriteMax", 128);
	maxPlaneSprite = (int)(gd->GetNum(total));
	strncpy(total, pre, 64);
	strncat(total, "TurretMax", 128);
	maxTurSprite = (int)(gd->GetNum(total));
	for(int itor = 0; itor < maxPlaneSprite; itor++){
			// delete old image
		destroy_bitmap(imgPlane[itor]);
			// create path
		strncpy(total, pre, 64);
		strncat(total, "Sprite", 128);
		imgPlane[itor] = load_bitmap(gd->GetStr(total,itor), NULL);
			// verify load
		if( imgPlane[itor] == NULL) imgPlane[itor] = create_bitmap(32,32);
	}
	for(int itor = 0; itor < maxTurSprite; itor++){
		// delete old image
		destroy_bitmap(imgTurret[itor]);
			// create path
		strncpy(total, pre, 64);
		strncat(total, "Turret", 128);
		imgTurret[itor] = load_bitmap(gd->GetStr(total,itor), NULL);
			// verify load
		if( imgTurret[itor] == NULL) imgTurret[itor] = create_bitmap(32,32);
	}

}

/// I am the plane update function
/// I manuver the plane arround the world based 
/// on the player's input.
void Plane::Update(){
		// update animation frames 
	if(curTurSprite > 0) curTurSprite -= 1;
	curPlaneSprite += 1;
	if( curPlaneSprite >= maxPlaneSprite) curPlaneSprite = 0;
		// invoke update funcitons
	UpdateInput();
	UpdateShoot();
}


/// I am the input processor function. I modify te plane's internal
/// values to 
void Plane::UpdateInput(){
		// impliment acceleration
	if(inUp){
		speed += DltSpeed;
		if(speed > MaxSpeed) speed = MaxSpeed;
	}else if(inDown){
		speed -= DltSpeed;
		if(speed < MinSpeed) speed = MinSpeed;
	}else{ //dampen back to average
		if(speed > AvgSpeed){
			speed -= DltSpeed;
		}else if(speed < AvgSpeed){
			speed += DltSpeed;
		}
			// force average speed
		if( AvgSpeed - speed < DltSpeed && AvgSpeed - speed > -DltSpeed){
			speed = AvgSpeed;
		}
	}
		// impliment straifing
	if(inRstrafe && canStrafe){
		strafe += DltSpeed;
		if(strafe > MaxSpeed) strafe = MaxSpeed;
	}else if(inLstrafe&& canStrafe){
		speed -= DltSpeed;
		if(strafe < MinSpeed) strafe = MinSpeed;
	}else{ //dampen back to 0
		if(strafe > 0){
			strafe -= DltSpeed;
		}else if(strafe < 0){
			strafe += DltSpeed;
		}
		// force average speed
		if( strafe < DltSpeed && strafe > -DltSpeed){
			strafe = 0;
		}
	}

	// impliment turning
	if(inRight){
		turnRate -= DltTurn;
		if(turnRate < -MaxTurn) turnRate = -MaxTurn;
	}else if( inLeft){
		turnRate += DltTurn;
		if(turnRate > MaxTurn) turnRate = MaxTurn;
	}else{	// ballance turn
		if(turnRate > DltTurn){
			turnRate -= DltTurn/2;
		}else if(turnRate < -DltTurn){
			turnRate += DltTurn/2;
		} else{ // close enough to level to level out
			turnRate = 0;
		}
	}
		// do rotation
	angle += turnRate;
	if( angle > 180 ) angle -= 360;
	if( angle < -180 ) angle += 360;
		// move plane to old heading value
	center->Copy(heading);
		// set new heading value according to input values
	heading->x = strafe;
	heading->y = speed;
	heading->RotateBy(angle);
		// put heading relative to center
	heading->x += center->x;
	heading->y += center->y;

	

	
}

/// I am the shooting funciton I check all the paramiters 
/// to see if we can shoot. If sucessful I will invoke the
/// bullete's spawn on this type of amunition
void Plane::UpdateShoot(){
	if(curRefire > 0) curRefire -= 1;	// decriment refire counter
		// assume aim is in the field of fire unles prooven otherwise
	isInField = true;
		// find the angle relative to plane heading
	float gunAng = center->AngTo(aim);
		// find shooting distance
	float gunDist = center->DistTo(aim);
		// correct for 360 ring
	gunAng -= angle;
	if( gunAng > 180 ) gunAng -= 360;
	if( gunAng < -180 ) gunAng += 360;
	
		// correct aim if it strays outside of  limits
	if( gunAng < TurMinAng || gunAng > TurMaxAng){
		isInField = false;
		gunAng = (TurMinAng+TurMaxAng)/2;	// put the gun ang dead center
	}
	if( gunDist < TurMinRange){
		isInField = false;
		gunDist = TurMinRange;
	}
	if( gunDist > TurMaxRange){
		isInField = false;
		gunDist = TurMaxRange;
	}


		// put aim inside the weapon bracket
	aim->x = 0;
	aim->y = gunDist;
	aim->RotateBy(gunAng+angle);
	
		// put aim relative to center
	aim->x += center->x;
	aim->y += center->y;

	

		// check if we can still shoot
	if(curRefire > 0) return;	// not reloaded
	if(inShoot == false) return;	// trigger not pulled
	if( isInField == false && TurBlindShot == false)return;
		// if we get here we can shoot the gun
	float bulletSpeed = 20;
		// shoot TurBulPerShot staggered like they came out ireguarly
	for(int itor = 0; itor < TurBulPerShot; itor++){
			// set helper to the start point of the bullet
		//helper->x = 0;
		//helper->y = bulletSpeed + bulletSpeed*itor/TurBulPerShot;
		//helper->RotateBy(gunAng);
		//helper->x += center->x;
		//helper->y += center->y;
		helper->Copy(center);
			//aim wiggle
			//
		float wiggle = sin(TurSprayAng*3.1415/360)*gunDist;
		float oldAimX = aim->x;
		float oldAimY = aim->y;
			// todo shift aim by random wiggle
		aim->x += wiggle*(float)(50-rand()%100 )/100.0;
		aim->y += wiggle*(float)(50-rand()%100 )/100.0;
			// todo use helper and aim points to fire a projectile
		Bullet * shot = myBulletCtrl->Spawn(helper, aim,0,0);
		
		aim->x = oldAimX;
		aim->y = oldAimY;
		float mul = (float)itor/(float)TurBulPerShot;
		shot->center->x += ((center->x-heading->x)+shot->dx)*mul;
		shot->center->y += ((center->y-heading->y)+shot->dy)*mul;

	}
		// set Turret animation
	if(curTurSprite == 0)curTurSprite = maxTurSprite-1;
		// set refire
	curRefire = TurRefire;


}

/// I am the camera setter function. I move the camera relative to
/// the aircraft and the mouse.
void Plane::SetCamera(Camera * cam){
		// how far we will move from the center
	float ofsX = gd->mouseX - gd->myCam->screen->w/2;
	float ofsY = gd->mouseY - gd->myCam->screen->h/2;
		// multiply by ofset ammount
	ofsX *= 0.8;
	ofsY *= 0.8;
	cam->center->x = center->x + ofsX;
	cam->center->y = center->y + ofsY;
}

/// I am the plane render function
void Plane::DrawPlane(Camera * cam){
	float turAng = -center->AngTo(aim);
	// draw to rend so we can scale it
	// draw turret
	// clear render to color
	clear_to_color(render, makecol(255,0,255));

	pivot_sprite(render, imgTurret[curTurSprite], 
		render->w/2, render->h/2, 
		imgTurret[curTurSprite]->w/2, imgTurret[curTurSprite]->h/2,
		ftofix(turAng*256/360));

	pivot_sprite(render, imgPlane[curPlaneSprite], 
		render->w/2, render->h/2, 
		imgPlane[curPlaneSprite]->w/2, imgPlane[curPlaneSprite]->h/2,
		ftofix(-angle*256/360));

	// scale render to 
	//cam->WorldDrawSprite(render,center->x, center->y);

	
	cam->WorldPivotScaleCtr(render ,center, 0, rendZoom);


}

/// todo render hud elements with regard to the aircraft
void Plane::DrawScreen(Camera * cam){

		// draw X


}